package com.yang.consumer;

import com.yang.message.Demo12Message;
import com.rabbitmq.client.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * 在 RabbitMQ 中，Consumer有两种消息确认的方式：
 * 方式一，自动确认。
 * 方式二，手动确认。
 * 对于自动确认的方式，RabbitMQ Broker只要将消息写入到TCP Socket中成功，就认为该消息投递成功，而无需Consumer手动确认
 * 对于手动确认的方式，RabbitMQ Broker将消息发送给Consumer之后，由Consumer手动确认之后，才任务消息投递成功。
 *
 * 实际场景下，因为自动确认存在可能丢失消息的情况，所以在对可靠性有要求的场景下，我们基本采用手动确认。当然，如果允许消息有一定的丢失，对性能有更高的场景下，可以考虑采用自动确认。
 */
@Component
@RabbitListener(queues = Demo12Message.QUEUE)
public class Demo12Consumer {

    private Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * 在消费方法上，我们增加类型为Channel的方法参数，和deliveryTag。通过调用其Channel#basicAck(deliveryTag, multiple)方法，可以进行消息的确认。
     * 在消费逻辑中，我们故意只提交消费的消息的Demo2Message.id为奇数的消息。
     * 这样，我们只需要发送一条id=1，一条id=2的消息，如果第二条的消息进度没有被提交，就可以说明手动提交消费进度成功。
     * @param message
     * @param channel
     * @param deliveryTag
     * @throws IOException
     */
    @RabbitHandler
    public void onMessage(Demo12Message message, Channel channel,
                          @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) throws IOException {
        logger.info("[onMessage][线程编号:{} 消息内容：{}]", Thread.currentThread().getId(), message);
        // 提交消费进度
        if (message.getId() % 2 == 1) {
            // ack 确认消息
            // 第二个参数 multiple ，用于批量确认消息，为了减少网络流量，手动确认可以被批处。
            // 1. 当 multiple 为 true 时，则可以一次性确认 deliveryTag 小于等于传入值的所有消息
            // 2. 当 multiple 为 false 时，则只确认当前 deliveryTag 对应的消息
            channel.basicAck(deliveryTag, false);
        }
    }

}